package com.mall.user.services;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.mall.commons.tool.exception.ProcessException;
import com.mall.commons.tool.exception.ValidateException;
import com.mall.user.IUserVerifyService;
import com.mall.user.constants.SysRetCodeConstants;
import com.mall.user.converter.UserConverterMapper;
import com.mall.user.dal.entitys.Member;
import com.mall.user.dal.entitys.UserVerify;
import com.mall.user.dal.persistence.MemberMapper;
import com.mall.user.dal.persistence.UserVerifyMapper;
import com.mall.user.dto.*;
import com.mall.user.utils.ExceptionProcessorUtils;
import com.mall.user.utils.JwtTokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import tk.mybatis.mapper.entity.Example;


import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @program: dragon-mall
 * @description:
 * @author: Keyu Li
 * @create: 2021-12-11 22:06
 **/


@Slf4j
@Component
@Service
public class UserVerifyServiceImpl implements IUserVerifyService {
    // 存储密码加盐
    private static final String SALT_LAYER_1 = "dragon4";
    private static final String SALT_LAYER_2 = "fly2";
    private static final String SALT_LAYER_3 = "7world";

    // token相关
    public static String ACCESS_TOKEN = "access_token";
    public static String USER_INFO_KEY = "userInfo";

    @Autowired
    private MemberMapper memberMapper;

    @Autowired
    private UserVerifyMapper verifyMapper;

    @Autowired
    UserConverterMapper userConverter;

    @Autowired
    JavaMailSender mailSender;

    @Override
    public UserRegisterResponse registerUser(UserRegisterRequest request) {
        UserRegisterResponse response = new UserRegisterResponse();
        try {
            // 1 检查参数
            request.requestCheck();
            // 2 查询是否有重名的 username和 email
            Example example = new Example(Member.class);
            example.createCriteria().orEqualTo("username", request.getUserName())
                    .orEqualTo("email", request.getEmail());
            List<Member> members = memberMapper.selectByExample(example);
            if (members.size() > 0) {
                // 查询出来有重名的 username 或 email
                throw new ValidateException(SysRetCodeConstants.USER_ALREADY_EXISTS.getCode(),
                        SysRetCodeConstants.USER_ALREADY_EXISTS.getMessage());
            }


            // 3 构造 member数据，插入到 tb_member表中
            Member member = new Member();
            member.setUsername(request.getUserName());
            member.setEmail(request.getEmail());
            //    3.1 password 进行加盐处理
            String securePwd = md5SaltPwd(request.getUserPwd());
            member.setPassword(securePwd);
            member.setCreated(new Date());
            member.setUpdated(new Date());
            member.setIsVerified("N");
            member.setState(1);
            int affectRows = memberMapper.insertSelective(member);
            if (affectRows != 1) {
                // 如果插入不是一行 ---> 为什么不 rollback
                throw new ProcessException(SysRetCodeConstants.USER_REGISTER_FAILED.getCode(),
                        SysRetCodeConstants.USER_REGISTER_FAILED.getMessage());
            }

            // 4.构造 userVerify数据, 插入到 tb_user_verify表
            UserVerify userVerify = new UserVerify();
            userVerify.setUsername(member.getUsername());
            //    4.1 生成用户唯一uuid: 通过 用户名+密码+随机uuid ---> MD5
            String key = member.getUsername() + member.getPassword() + UUID.randomUUID();
            String uuid = DigestUtils.md5DigestAsHex(key.getBytes());
            userVerify.setUuid(uuid);
            userVerify.setRegisterDate(new Date());
            userVerify.setIsVerify("N");
            userVerify.setIsExpire("N");
            affectRows = verifyMapper.insertSelective(userVerify);
            if (affectRows != 1) {
                // 如果插入不是一行 ---> 为什么不&怎么rollback
                throw new ProcessException(SysRetCodeConstants.USER_REGISTER_FAILED.getCode(),
                        SysRetCodeConstants.USER_REGISTER_FAILED.getMessage());
            }

            // 5.向用户发送激活邮件 --> http://localhost:8080?uuid=xxx&userName=xxx
            //TODO: 1.如何设置邮件激活链接有效期；
            //      2.使用 mq异步发送邮件
            sendEmail(uuid, member);

            // 6 封装操作成功的response
            response.setCode(SysRetCodeConstants.SUCCESS.getCode());
            response.setMsg(SysRetCodeConstants.SUCCESS.getMessage());
            log.info("注册成功");
        } catch (Exception e) {
            // 接收抛出的那些异常并处理
            e.printStackTrace();
            log.error("UserVerifyServiceImpl.register exception: {}", e.getCause());
            ExceptionProcessorUtils.wrapperHandlerException(response, e);
        }
        return response;
    }

    private void sendEmail(String uuid, Member member) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setSubject("dragon-mall用户注册验证");
        //message.setFrom("register@dragon.com");
        message.setFrom("kyle_luyu@163.com");
        message.setTo(member.getEmail());
        // 设置文本内容
        StringBuilder sb = new StringBuilder();
        // 获取2天后的日期
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.DAY_OF_MONTH, 2);
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String verifyDeadLine = dateFormat.format(calendar.getTime());
        sb.append("请点击以下链接以激活您的账户：http://localhost:8080/user/verify?uuid=").append(uuid)
                .append("&userName=").append(member.getUsername())
                .append("\n请无比确保在 ").append(verifyDeadLine)
                .append(" 前注册，过期失效！");
        message.setText(sb.toString());
        mailSender.send(message);
        log.info("给 {} 发送邮件成功", member.getEmail());
    }

    @Override
    public UserLoginResponse login(UserLoginRequest request) {
        UserLoginResponse response = new UserLoginResponse();
        try {
            // 1 参数校验
            request.requestCheck();

            // 2 获取加密后的密码
            String securePwd = md5SaltPwd(request.getPassword());

            // 3 比对数据库中数据
            Example example = new Example(Member.class);
            example.createCriteria().andEqualTo("username", request.getUserName()).andEqualTo("password", securePwd);
            List<Member> members = memberMapper.selectByExample(example);
            //    3.1 如果没有查出来，或者查出来好几个
            if (members.size() != 1) {
                throw new ValidateException(SysRetCodeConstants.USERORPASSWORD_ERRROR.getCode(),
                        SysRetCodeConstants.USERORPASSWORD_ERRROR.getMessage());
            }
            //    3.2 如果当前用户没有验证
            Member member = members.get(0);
            if ("N".equals(member.getIsVerified())) {
                throw new ValidateException(SysRetCodeConstants.USER_ISVERFIED_ERROR.getCode(),
                        SysRetCodeConstants.USER_ISVERFIED_ERROR.getMessage());
            }

            // 4 封装操作成功的 UserLoginDto
            //    4.1 converter 把 member对象转换为 response
            response = userConverter.converter(member);
            //    4.2 设置 token：将uid，username和file都封装进去
            Map<Object, Object> userInfoMap = new HashMap<>();
            userInfoMap.put("uid", member.getId());
            userInfoMap.put("userName", member.getUsername());
            userInfoMap.put("file", member.getFile());
            String token = JwtTokenUtils.builder().msg(new Gson().toJson(userInfoMap)).build().creatJwtToken();
            response.setToken(token);
            response.setCode(SysRetCodeConstants.SUCCESS.getCode());
            response.setMsg(SysRetCodeConstants.SUCCESS.getMessage());

        } catch (Exception e) {
            // 接收抛出的那些异常并处理
            log.error("UserVerifyServiceImpl.login(post) exception: {}", e.getMessage());
            ExceptionProcessorUtils.wrapperHandlerException(response, e);
        }
        return response;
    }


    private String md5SaltPwd(String plainPwd) {
        // 第一层
        String l1 = DigestUtils.md5DigestAsHex((plainPwd + SALT_LAYER_1).getBytes());
        // 第二层
        String l2 = DigestUtils.md5DigestAsHex((l1 + SALT_LAYER_2).getBytes());
        // 第三层
        return DigestUtils.md5DigestAsHex((l2 + SALT_LAYER_3).getBytes());
    }


    @Override
    public UserGetLoginResponse getLogin(UserGetLoginRequest request) {
        UserGetLoginResponse response = new UserGetLoginResponse();
        try {
            // 1 验证参数
            request.requestCheck();

            // 2 根据token解析出 username,顺便记录一笔日志
            String userInfoMapJson = JwtTokenUtils.builder().token(request.getToken()).build().freeJwt();
            JSONObject object = JSON.parseObject(userInfoMapJson);
            Long uid = Long.parseLong(object.get("uid").toString());
            String file = object.get("file") == null ? null : object.get("file").toString();
            String userName = object.get("userName").toString();
            log.info("用户退出登录 --> 用户id:{}, 用户名:{}, 时间:{}", uid, userName, new Date());

            // 3 封装结果到 UserGetLoginDto
            UserGetLoginDto loginDto = new UserGetLoginDto();
            loginDto.setUid(uid);
            loginDto.setFile(file);

            // 4 封装 dto到 UserGetLoginResponse并返回
            response.setGetLoginDto(loginDto);
            response.setCode(SysRetCodeConstants.SUCCESS.getCode());
            response.setMsg(SysRetCodeConstants.SUCCESS.getMessage());

        } catch (Exception e) {
            // 接收抛出的那些异常并处理
            log.error("UserVerifyServiceImpl.getLogin exception: {}", e.getMessage());
            ExceptionProcessorUtils.wrapperHandlerException(response, e);
        }
        return response;
    }


    /*
     * 退出登录不需要 service层操作
     * */
    @Override
    public UserLogoutResponse logout(UserLogoutRequest request) {
        UserLogoutResponse response = new UserLogoutResponse();
        try {
            // 1 验证参数
            request.requestCheck();

            // 2 根据token解析出 userInfoMap,顺便记录一笔日志
            String userInfoMapJson = JwtTokenUtils.builder().token(request.getToken()).build().freeJwt();
            HashMap<String, String> userInfoMap = new Gson().fromJson(userInfoMapJson, HashMap.class);
            log.info("用户退出登录 --> 用户id:{}, 用户名:{}, 时间:{}", userInfoMap.get("uid"), userInfoMap.get("userName"), new Date());

            // 3 封装response返回
            response.setCode(SysRetCodeConstants.SUCCESS.getCode());
            response.setMsg(SysRetCodeConstants.SUCCESS.getMessage());
        } catch (Exception e) {
            // 接收抛出的那些异常并处理
            log.error("UserVerifyServiceImpl.getLogin exception: {}", e.getMessage());
            ExceptionProcessorUtils.wrapperHandlerException(response, e);
        }
        return response;
    }

    @Override
    public UserVerifyResponse verify(UserVerifyRequest request) {
        UserVerifyResponse response = new UserVerifyResponse();
        try {
            // 1 参数检验
            request.requestCheck();

            // 2 更改 tb_user_verify表，设置已经激活
            Example userVerifyExample = new Example(UserVerify.class);
            userVerifyExample.createCriteria().andEqualTo("username", request.getUserName())
                    .andEqualTo("uuid", request.getUuid());
            UserVerify userVerifyY = new UserVerify();
            userVerifyY.setIsVerify("Y");
            int verifyRows = verifyMapper.updateByExampleSelective(userVerifyY, userVerifyExample);
            if (verifyRows != 1) {
                throw new ValidateException(SysRetCodeConstants.USERVERIFY_INFOR_INVALID.getCode(),
                        SysRetCodeConstants.USERVERIFY_INFOR_INVALID.getMessage());
            }

            // 3 更改 tb_member表
            Example memberExample = new Example(Member.class);
            memberExample.createCriteria().andEqualTo("username", request.getUserName());
            Member memberY = new Member();
            memberY.setIsVerified("Y");
            int memberRows = memberMapper.updateByExampleSelective(memberY, memberExample);
            if (memberRows != 1) {
                throw new ValidateException(SysRetCodeConstants.USERVERIFY_INFOR_INVALID.getCode(),
                        SysRetCodeConstants.USERVERIFY_INFOR_INVALID.getMessage());
            }

            // 4 封装返回 response
            response.setCode(SysRetCodeConstants.SUCCESS.getCode());
            response.setMsg(SysRetCodeConstants.SUCCESS.getMessage());

        } catch (Exception e) {
            log.error("UserVerifyServiceImpl.verify exception: {}", e.getMessage());
            ExceptionProcessorUtils.wrapperHandlerException(response, e);
        }
        return response;
    }

    @Override
    public CheckAuthResponse validToken(CheckAuthRequest request) {
        CheckAuthResponse response = new CheckAuthResponse();
        try {
            // 1 验证参数
            request.requestCheck();

            // 2 根据token解析出 userInfoMap,顺便记录一笔日志
            String userInfoMapJson = JwtTokenUtils.builder().token(request.getToken()).build().freeJwt();
            JSONObject jsonObject = JSON.parseObject(userInfoMapJson);
            Long uid = Long.parseLong(jsonObject.get("uid").toString());
            String userName = jsonObject.get("userName").toString();

            // 3 封装返回 response
            HashMap<String, Object> userinfo = new HashMap<>();
            userinfo.put("uid", uid);
            userinfo.put("userName", userName);
            response.setUserinfo(userinfo);
            response.setCode(SysRetCodeConstants.SUCCESS.getCode());
            response.setMsg(SysRetCodeConstants.SUCCESS.getMessage());
        } catch (Exception e) {
            log.error("UserVerifyServiceImpl.validToken exception: {}", e.getMessage());
            ExceptionProcessorUtils.wrapperHandlerException(response, e);
        }
        return response;
    }
}
